/*
	Copyright (C) 1994 Sean Luke

	COWSIPCLibrary.m
	Version 10
	Sean Luke
	
*/




#import "COWSIPCLibrary.h"
#import <stdio.h>

@implementation COWSIPCLibrary


- init
	{
	char nm[COWSLARGESTPATHLENGTH];	
	id returnval=[super init];
	sprintf (nm,"%s(COWS)",[NXApp appName]);
	connection = [NXConnection registerRoot: self withName:nm];
	[connection runFromAppKit];
	arguments=[[COWSArgumentList alloc] init];
	arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;
	result=[[COWSStringNode alloc] init];
	return returnval;
	}
	
	
- free
	{
	if (connection!=NULL) [connection free];
	[arguments free];
	[result free];
	[NXConnection removeObject:self];
	return [super free];
	}


- loadLibrary:sender
	{
	id returnval=[super loadLibrary:sender];
	
	if (![sender conformsTo:@protocol(LibraryControl)])
		{
		printf ("StandardLibrary error:  Interpreter cannot accept Library Control protocol!\n");
		return NULL;
		}
	[sender addLibraryFunction:"send-out"
			selector:@selector(ipc_sendout:)
			target:self];
	[sender addLibraryFunction:"send-out-remote"
			selector:@selector(ipc_sendout_machine:)
			target:self];
	[sender addLibraryFunction:"send"
			selector:@selector(ipc_send:)
			target:self];
	[sender addLibraryFunction:"send-remote"
			selector:@selector(ipc_send_machine:)
			target:self];
	[sender addLibraryFunction:"launch"
			selector:@selector(ipc_launch:)
			target:self];
	[sender addLibraryFunction:"launched?"
			selector:@selector(ipc_launched:)
			target:self];
	interpreter=sender;	
	return returnval;
	}


- ipc_send:arg_list
	{
	char nm[COWSLARGESTPATHLENGTH];	
	id return_val=[[COWSStringNode alloc] init];
	id current;
	id name=[[COWSStringNode alloc] init];
	id function=[[COWSStringNode alloc] init];
	id server;
	
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"send error:  nothing to connect to"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		[return_val setString:"send error:  no function to call"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[function setString:[current string]];
		[current free];
		}
	sprintf (nm,"%s(COWS)",[name string]);
	server = [NXConnection connectToName:nm];
	if (server==nil)
		{
		[return_val setString:"send error:  cannot establish connection"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}

	if (![server conformsTo:@protocol(InterpreterIPC)])
		{
		[return_val setString:"send error:  remote object is not an interpreter"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}

	if (![server isForeground])
		{
		[return_val setString:
			"send error:  remote interpreter is in background mode"];
		[return_val setError:YES];	
		[name free];
		[function free];
		return return_val;
		}

	while ([arg_list top]!=NULL)
		{
		id current=[arg_list pop];
		[server addArgument:(const char*)[current string]];
		[current free];
		}

	[server sendFunction:(const char*) [function string]:return_val];

	[function free];
	[name free];
		
	return return_val;
	}




- ipc_send_machine:arg_list
	{
	char nm[COWSLARGESTPATHLENGTH];	
	id return_val=[[COWSStringNode alloc] init];
	id current;
	id name=[[COWSStringNode alloc] init];
	id function=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	id server;
	
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"send-remote error:  no machine to connect to"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[machine setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"send-remote error:  nothing to connect to"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		[return_val setString:"send-remote error:  no function to call"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[function setString:[current string]];
		[current free];
		}

	sprintf (nm,"%s(COWS)",[name string]);
	server = [NXConnection connectToName:nm onHost:[machine string]];
	if (server==nil)
		{
		[return_val setString:"send-remote error:  cannot establish connection"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}

	if (![server conformsTo:@protocol(InterpreterIPC)])
		{
		[return_val setString:"send-remote error:  remote object is not an interpreter"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}

	if (![server isForeground])
		{
		[return_val setString:
			"send-remote error:  remote interpreter is in background mode"];
		[return_val setError:YES];	
		[name free];
		[machine free];
		[function free];
		return return_val;
		}

	while ([arg_list top]!=NULL)
		{
		id current=[arg_list pop];
		[server addArgument:(const char*)[current string]];
		[current free];
		}

	[server sendFunction:(const char*) [function string]:return_val];

	[name free];
	[machine free];
	[function free];
		
	return return_val;
	}	
	



- ipc_sendout:arg_list					// calls a remote function in another
										// interpreter and ignores answer.

	{
	char nm[COWSLARGESTPATHLENGTH];	
	id return_val=[[COWSStringNode alloc] init];
	id current;
	id name=[[COWSStringNode alloc] init];
	id function=[[COWSStringNode alloc] init];
	id server;
	
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"send-out error:  nothing to connect to"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		[return_val setString:"send-out error:  no function to call"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[function setString:[current string]];
		[current free];
		}
	sprintf (nm,"%s(COWS)",[name string]);
	server = [NXConnection connectToName:nm];
	if (server==nil)
		{
		[return_val setString:"send-out error:  cannot establish connection"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}

	if (![server conformsTo:@protocol(InterpreterIPC)])
		{
		[return_val setString:"send-out error:  remote object is not an interpreter"];
		[return_val setError:YES];
		[name free];
		[function free];
		return return_val;
		}

	while ([arg_list top]!=NULL)
		{
		id current=[arg_list pop];
		[server addArgument:(const char*)[current string]];
		[current free];
		}

	[server sendOutFunction:(const char*) [function string]];
	[function free];
	[name free];
		
	return return_val;
	}
	
	
- ipc_sendout_machine:arg_list
	{
	char nm[COWSLARGESTPATHLENGTH];	
	id return_val=[[COWSStringNode alloc] init];
	id current;
	id name=[[COWSStringNode alloc] init];
	id function=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	id server;
	
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"send-out-remote error:  no machine to connect to"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[machine setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"send-out-remote error:  nothing to connect to"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		[return_val setString:"send-out-remote error:  no function to call"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[function setString:[current string]];
		[current free];
		}

	sprintf (nm,"%s(COWS)",[name string]);
	server = [NXConnection connectToName:nm onHost:[machine string]];
	if (server==nil)
		{
		[return_val setString:"send-out-remote error:  cannot establish connection"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}

	if (![server conformsTo:@protocol(InterpreterIPC)])
		{
		[return_val setString:"send-out-remote error:  remote object is not an interpreter"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}

	while ([arg_list top]!=NULL)
		{
		id current=[arg_list pop];
		[server addArgument:(const char*)[current string]];
		[current free];
		}

	[server sendOutFunction:(const char*) [function string]];
	[name free];
	[machine free];
	[function free];
		
	return return_val;
	}
	
	
	
	
	
// IPC MESSAGES


- finishedInterpreting:(const char*)returnValue:(int)thisMessage:sender
	// this message is sent to the delegate, if it can receive it, after
	// the interpreter has finished interpreting a function request.
	// it is defined here for IPC purposes (an interpreter can be its
	// own delegate in an IPC process.
	{
	[result setString:returnValue];
	[result setError:NO];
	return self;
	}

- errorInterpreting:(int) thisError:(const char*)thisFunction:
	(int)thisPosition:(const char*)thisString:sender
	// this message is sent to the delegate, if it can receive it, after
	// the interpreter has encountered an error while interpreting.
	{
	[result setString:thisString];
	[result setError:YES];
	return self;
	}




- addArgument:(const char*)this_argument
	{
	id new_string;
	printf ("Adding Argument: %s\n",this_argument);
	if (arguments_state==COWSIPCLIBRARY_ARGSTATE_WORKING)
		{
		printf ("COWS IPC Error:  trying to begin new function before old one has finished\n");
		return NULL;
		}
	
	new_string=[[COWSStringNode alloc] init];
	[new_string setString:this_argument];
	[arguments push:new_string];
	printf ("Added Argument:  %s\n",[new_string string]);
	return NULL;
	}



- (oneway void) sendOutFunction:(const char*) this_name
	{
	id arg_list=[[COWSArgumentList alloc] init];

	if (arguments_state==COWSIPCLIBRARY_ARGSTATE_WORKING)
		{
		printf ("COWS IPC Error:  calling new function before old one has finished\n");
		}
	else
		{
		arguments_state=COWSIPCLIBRARY_ARGSTATE_WORKING;
		
		// reverse the argument list	
				
		while([arguments top]!=NULL)
			{
			[arg_list push:[arguments pop]];
			}
		
		if (![interpreter locked]&&![interpreter working])
			// i.e., interpreter is able to respond to messages
			{
			// note no delegate is assigned.
			[interpreter setDelegate:NULL];
			[interpreter interpretFunction:this_name arguments:arg_list];
			}
		
		[arguments clear];
		arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;
		}
	[arg_list free];
	}





- sendFunction:(const char*) this_name:reply
	{
	id arg_list=[[COWSArgumentList alloc] init];
	
	if (arguments_state==COWSIPCLIBRARY_ARGSTATE_WORKING)
		{
		char junk[COWSLARGESTPATHLENGTH+40];
		sprintf (junk,"remote send error (%s):  calling new function before old one has finished.", [NXApp appName]);		
		[reply setString:junk];
		[reply setError:YES];	
		}
	else
		{
		arguments_state=COWSIPCLIBRARY_ARGSTATE_WORKING;
		
		// clean out response
		
		[result setString:""];
		[result setError:NO];
		
		// reverse the argument list	
				
		while([arguments top]!=NULL)
			{
			[arg_list push:[arguments pop]];
			}
		
		if (![interpreter locked]&&![interpreter working])
			{
			[interpreter setDelegate:self];
			[interpreter interpretFunction:this_name arguments:arg_list];
			[reply setString:(const char*)[result string]];
			[reply setError:[result error]];
			}
		else
			{
			char junk[COWSLARGESTPATHLENGTH+40];
			sprintf (junk,"remote send error (%s):  interpreter is unavailable.", [NXApp appName]);
			[reply setString: junk];
			[reply setError:YES];	
			}
		arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;
		}
	[arg_list free];
	return NULL;
	}


- (BOOL) isForeground
	{
	if (interpreter==NULL) 
		{
		printf ("Yikes!  No Interpreter\n");
		return NO;
		}
	return [interpreter foreground];
	}

- pauseCancelled:sender
	{
	return NULL;
	}

- pauseInterpreter:remote_process
	{
	[interpreter pauseInterpreting:remote_process];
	return NULL;
	}
	
	
	
- ipc_launch:arg_list
	{
	id return_val=[[COWSStringNode alloc] init];
	id current;
	id name=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	port_t response;
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"launch error:  nothing to launch"];
		[return_val setError:YES];
		[name free];
		[machine free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		char host[MAXHOSTNAMELEN];
		gethostname(host,MAXHOSTNAMELEN);
		[machine setString:(const char*) host];
		}
	else
		{
		current=[arg_list pop];
		[machine setString:[current string]];
		[current free];
		}

	response=NXPortFromName([name string],[machine string]);

	if (response==PORT_NULL)
		{
		[return_val setString:"f"];
		[name free];
		[machine free];
		return return_val;
		}

	[return_val setString:"t"];
	[name free];
	[machine free];
	
	return return_val;
	}



- ipc_launched:arg_list
	{
	id return_val=[[COWSStringNode alloc] init];
	id current;
	id name=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	port_t response;
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"launched? error:  nothing to check for"];
		[return_val setError:YES];
		[name free];
		[machine free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		char host[MAXHOSTNAMELEN];
		gethostname(host,MAXHOSTNAMELEN);
		[machine setString:(const char*) host];
		}
	else
		{
		current=[arg_list pop];
		[machine setString:[current string]];
		[current free];
		}

	response=NXPortNameLookup([name string],[machine string]);

	if (response==PORT_NULL)
		{
		[return_val setString:"f"];
		[name free];
		[machine free];
		return return_val;
		}

	[return_val setString:"t"];
		
	[name free];
	[machine free];
	return return_val;
	}


@end
